package sunyu.kit.encrypt;

import org.apache.shiro.codec.Base64;
import org.apache.shiro.codec.CodecSupport;
import org.apache.shiro.codec.Hex;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.apache.shiro.crypto.hash.Sha512Hash;
import org.slf4j.Logger;
import sunyu.kit.common.ExceptionKit;
import sunyu.kit.common.LogKit;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;

/**
 * 加密解密工具类
 *
 * @author 孙宇
 */
public class EncryptKit {

    public static final String salt = "!(*#)#!&";//加密时使用的干扰码(盐)
    /*
     * 算法                           模式      填充 16字节加密后数据长度 不满16字节加密后长度
     * AES/CBC/NoPadding             16        不支持
     * AES/CBC/PKCS5Padding          32        16
     * AES/CBC/ISO10126Padding       32        16
     * AES/CFB/NoPadding             16        原始数据长度
     * AES/CFB/PKCS5Padding          32        16
     * AES/CFB/ISO10126Padding       32        16
     * AES/ECB/NoPadding             16        不支持
     * AES/ECB/PKCS5Padding          32        16
     * AES/ECB/ISO10126Padding       32        16
     * AES/OFB/NoPadding             16        原始数据长度
     * AES/OFB/PKCS5Padding          32        16
     * AES/OFB/ISO10126Padding       32        16
     * AES/PCBC/NoPadding            16        不支持
     * AES/PCBC/PKCS5Padding         32        16
     * AES/PCBC/ISO10126Padding      32        16
     *
     * 注：
     * 1、JCE中AES支持五中模式：CBC，CFB，ECB，OFB，PCBC；支持三种填充：NoPadding，PKCS5Padding，ISO10126Padding。
     *    不带模式和填充来获取AES算法的时候，其默认使用ECB/PKCS5Padding。
     * 2、Java支持的密钥长度：keySize must be equal to 128, 192 or 256
     * 3、Java默认限制使用大于128的密钥加密（解密不受限制），报错信息：java.security.InvalidKeyException: Illegal key size or default parameters
     * 4、下载并安装JCE Policy文件即可突破128密钥长度的限制：覆盖jre\lib\security目录下local_policy.jar、US_export_policy.jar文件即可
     * 5、除ECB外，需提供初始向量（IV），如：Cipher.init(opmode, key, new IvParameterSpec(iv)), 且IV length: must be 16 bytes long
     */
    public static final String TRANSFORMATION = "AES/ECB/PKCS5Padding";//模式
    private static final Logger logger = LogKit.getLogger();
    private static final String defaultCharSet = "UTF-8";//默认字符集
    private static final String ALGORITHM = "AES";//运算法则
    private static final int KEY_SIZE = 128;


    /**
     * MD5
     *
     * @param text
     * @return
     */
    public static String md5(String text) {
        return new Md5Hash(text).toString();
    }

    /**
     * MD5
     *
     * @param text
     * @param salt 盐
     * @return
     */
    public static String md5(String text,
                             String salt) {
        return new Md5Hash(text, salt).toString();
    }

    /**
     * MD5
     *
     * @param text
     * @param salt           盐
     * @param hashIterations 散列次数
     * @return
     */
    public static String md5(String text,
                             String salt,
                             int hashIterations) {
        return new Md5Hash(text, salt, hashIterations).toString();
    }

    /**
     * Base64编码
     *
     * @param text
     * @return
     */
    public static String base64Encode(String text) {
        return Base64.encodeToString(CodecSupport.toBytes(text, defaultCharSet));
    }

    /**
     * Base64解码
     *
     * @param text
     * @return
     */
    public static String base64Decode(String text) {
        return Base64.decodeToString(CodecSupport.toBytes(text, defaultCharSet));
    }

    /**
     * Hex编码
     *
     * @param text
     * @return
     */
    public static String hexEncode(String text) {
        return Hex.encodeToString(CodecSupport.toBytes(text, defaultCharSet));
    }

    /**
     * Hex解码
     *
     * @param text
     * @return
     */
    public static String hexDecode(String text) {
        return new String(Hex.decode(CodecSupport.toBytes(text, defaultCharSet)));
    }

    /**
     * sha256
     *
     * @param text
     * @return
     */
    public static String sha256(String text) {
        return new Sha256Hash(text).toString();
    }

    /**
     * sha512
     *
     * @param text
     * @return
     */
    public static String sha512(String text) {
        return new Sha512Hash(text).toString();
    }

    /**
     * AES加密
     *
     * @param text
     * @param password
     * @return
     */
    public static String aesEncrypt(String text,
                                    String password) {
        try {
            return encrypt(text, password);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * AES解密
     *
     * @param text
     * @param password
     * @return
     */
    public static String aesDecrypt(String text,
                                    String password) {
        try {
            return decrypt(text, password);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 获取密钥
     *
     * @return
     * @throws Exception
     */
    public static Key getKey() throws Exception {
        //实例化
        KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM);
        //AES 要求密钥长度为128位、192位或256位
        kg.init(KEY_SIZE);
        //生成密钥
        SecretKey secretKey = kg.generateKey();
        return secretKey;
    }

    /**
     * 获得一个密钥
     *
     * @return
     * @throws Exception
     */
    public static String getKeyStr() {
        try {
            return org.apache.commons.codec.binary.Base64.encodeBase64String(getKey().getEncoded());
        } catch (Exception e) {
            logger.error(ExceptionKit.getStackTrace(e));
        }
        return null;
    }

    /**
     * 加密
     *
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public static String encrypt(String data,
                                 String key) throws Exception {
        byte[] dataBytes = data.getBytes(defaultCharSet);
        byte[] keyBytes = org.apache.commons.codec.binary.Base64.decodeBase64(key);
        return org.apache.commons.codec.binary.Base64.encodeBase64String(encrypt(dataBytes, keyBytes));
    }

    /**
     * 解密
     *
     * @param data 待解密数据
     * @param key  密钥
     * @return byte[] 解密数据
     * @throws Exception
     */
    public static String decrypt(String data,
                                 String key) throws Exception {
        return decrypt(org.apache.commons.codec.binary.Base64.decodeBase64(data), org.apache.commons.codec.binary.Base64.decodeBase64(key));
    }

    /**
     * 解密
     *
     * @param data 待解密数据
     * @param key  密钥
     * @return byte[] 解密数据
     * @throws Exception
     */
    private static String decrypt(byte[] data,
                                  byte[] key) throws Exception {
        //还原密钥
        Key k = new SecretKeySpec(key, ALGORITHM);
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        //初始化，设置解密模式
        cipher.init(Cipher.DECRYPT_MODE, k);
        //执行操作
        return new String(cipher.doFinal(data), defaultCharSet);
    }


    /**
     * 加密
     *
     * @param data 待加密数据
     * @param key  密钥
     * @return bytes[] 加密数据
     * @throws Exception
     */
    public static byte[] encrypt(byte[] data,
                                 byte[] key) throws Exception {
        //还原密钥
        Key k = new SecretKeySpec(key, ALGORITHM);
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        //初始化，设置为加密模式
        cipher.init(Cipher.ENCRYPT_MODE, k);
        //执行操作
        return cipher.doFinal(data);
    }

}
